from ccxtbt import CCXTStore
import backtrader as bt
from datetime import datetime, timedelta
import json
import time


class TestStrategy(bt.Strategy):

    def __init__(self):
        fast = bt.indicators.EMA(self.data0, period=8)
        slow = bt.indicators.EMA(self.data0, period=21)
        self.buy_sig = bt.indicators.CrossOver(fast, slow)

    def log(self, txt, dt=None):
        ''' Logging function fot this strategy'''

        print('%s, %s' % (datetime.fromtimestamp(dt/1000).strftime("%Y-%m-%d %H:%M:%S"), txt))

    def notify_order(self, order):
        dtstr = self.data.datetime.datetime().strftime("%Y-%m-%d")
        if order.status == order.Submitted:
            self.log('**NOTIFY** ORDER SUBMITTED', dt=order.created.dt)
            self.order = order
            return
        if order.status == order.Accepted:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            self.log('**NOTIFY** ORDER ACCEPTED', dt=order.created.dt)
            self.order = order
            return

        if order.status in [order.Expired]:
            self.log('**NOTIFY** ORDER BUY EXPIRED', dt=order.created.dt)

        elif order.status in [order.Completed]:
            if order.isbuy():
                self.log(
                    '**NOTIFY** ORDER BUY COMPLETED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                    (order.executed.price,
                     order.executed.size,
                     order.executed.comm), dt=order.executed.dt)

            else:  # Sell
                self.log('**NOTIFY** ORDER SELL COMPLETED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.size,
                          order.executed.comm), dt=order.executed.dt)
        elif order.status in [order.Margin]:
            self.log(
                '**NOTIFY** ORDER Failed, not cash', dt=order.executed.dt
            )

        # Sentinel to None: new orders allowed
        self.order = None

    def next(self):
        bar_dt = self.data.datetime.datetime() + timedelta(hours=8)
        if self.live_data:
            cash, size = self.broker.get_wallet_balance()
            value = size * self.data.close
            self.log("FETCH DATA: bar_dt: %s, symbol: %s, inds: %s" % (bar_dt, self.data._dataname, self.buy_sig[0]), dt=time.time() * 1000)
            if value <= 10 and self.buy_sig > 0:
                size = 50
                self.buy(exectype=bt.Order.Market, size=size)
                self.log('**CMD** ORDER BUY: bar_dt: %s, symbol: %s, size=%s' % (bar_dt, self.data._dataname, size), dt=time.time() * 1000)

            if value >= 10 and self.buy_sig < 0:
                self.sell(exectype=bt.Order.Market, size=size)
                self.log('**CMD** ORDER SELL: bar_dt: %s, symbol: %s, size=%s' % (bar_dt, self.data._dataname, size), dt=time.time() * 1000)


    def notify_data(self, data, status, *args, **kwargs):
        dn = data._name
        dt = datetime.now()
        msg = 'Data Status: {}'.format(data._getstatusname(status))
        print(dt, dn, msg)

        if data._getstatusname(status) == 'LIVE':
            self.live_data = True
        else:
            self.live_data = False


with open('/Users/wudi/Workspace/project/bt-ccxt-store/samples/params.json', 'r') as f:
    params = json.load(f)

cerebro = bt.Cerebro(quicknotify=True)


# Add the strategy
cerebro.addstrategy(TestStrategy)

# Create our store
config = {
    'apiKey': params["binance"]["apikey"],
    'secret': params["binance"]["secret"],
    'enableRateLimit': True,
}


# IMPORTANT NOTE - Kraken (and some other exchanges) will not return any values
# for get cash or value if You have never held any BNB coins in your account.
# So switch BNB to a coin you have funded previously if you get errors
store = CCXTStore(exchange='binance', currency="DOGE", config=config, retries=5, debug=False)


# Get the broker and pass any kwargs if needed.
# ----------------------------------------------
# Broker mappings have been added since some exchanges expect different values
# to the defaults. Case in point, Kraken vs Bitmex. NOTE: Broker mappings are not
# required if the broker uses the same values as the defaults in CCXTBroker.
broker_mapping = {
    'order_types': {
        bt.Order.Market: 'market',
        bt.Order.Limit: 'limit',
        bt.Order.Stop: 'stop-loss',  # stop-loss for kraken, stop for bitmex
        bt.Order.StopLimit: 'stop limit'
    },
    'mappings': {
        'closed_order': {
            'key': 'status',
            'value': 'closed'
        },
        'canceled_order': {
            'key': 'result',
            'value': 1
        }
    }
}

broker = store.getbroker(broker_mapping=broker_mapping)
cerebro.setbroker(broker)

# Get our data
# Drop newest will prevent us from loading partial data from incomplete candles
hist_start_date = datetime.utcnow() - timedelta(minutes=21)
data = store.getdata(dataname='DOGE/USDT', name="DOGEUSDT",
                     timeframe=bt.TimeFrame.Minutes, fromdate=hist_start_date,
                     compression=1, ohlcv_limit=30, drop_newest=True, debug=False) #, historical=True)

# Add the feed
cerebro.adddata(data)


# Run the strategy
cerebro.run()
